#include "usergame.h"

#define MIN_LEVEL   1
#define MAX_LEVEL   5

#define MAX_WIDTH 50
#define MAX_HEIGHT 50
#define MAX_CANDIDATES 30

uint8_t width;
uint8_t height;
uint8_t hWalls [(MAX_HEIGHT * MAX_WIDTH - 1) / 8 + 1];
uint8_t vWalls [(MAX_HEIGHT * MAX_WIDTH - 1) / 8 + 1];
uint8_t cells [(MAX_HEIGHT * MAX_WIDTH - 1) / 8 + 1];


int candidates [MAX_CANDIDATES];
uint8_t candidateCount;
static uint8_t level=1; //难度等级

int posX;
int posY;

int movingX;
int movingY;

const uint8_t game2logo[] = {64,16,
0xD5,0xFF,0x75,0xF7,0xDD,0x7D,0xFF,0x76,0xC0,0x05,0x54,0x00,0x05,0x00,0x50,0x16,
0xF7,0xFD,0xDF,0x5D,0xF5,0x75,0x5D,0xF6,0xC1,0x00,0x01,0x51,0x05,0x15,0x04,0x56,
0xF7,0x7D,0x57,0xFF,0xFF,0xF7,0x7F,0xDE,0xC0,0x11,0x57,0xFF,0xFF,0xF4,0x00,0x16,
0xF5,0xDD,0x76,0xEF,0xFF,0xF7,0x5D,0xD6,0xC4,0x45,0x16,0x49,0xC7,0x71,0x44,0x46,
0xF5,0xD5,0xFE,0xAC,0xF6,0xBD,0xDD,0x56,0xC4,0x55,0x06,0xAA,0xEE,0x71,0x15,0x56,
0xF7,0xFF,0xF6,0xE8,0xC7,0x37,0x77,0x56,0xC0,0x01,0x47,0xFF,0xFF,0xF1,0x01,0x56,
0xF7,0xF5,0x5F,0xFF,0xFF,0xF7,0xF5,0xF6,0xC0,0x44,0x41,0x40,0x54,0x40,0x14,0x56,
0xDF,0x77,0xFF,0x7F,0x57,0x7F,0x75,0xD6,0xC4,0x54,0x00,0x00,0x55,0x01,0x54,0x46,

};


void reset();
void handleMovement(uint8_t cmd);
void drawMaze();
void generateMaze(uint8_t w, uint8_t h);
void gridSet(uint8_t* grid, uint8_t row, uint8_t col, bool on);
void moveToCell(uint8_t row, uint8_t col);
int removeCandidate(int index);
bool isSet(uint8_t* grid, uint8_t row, uint8_t col);
void addCandidate(int val);


void game2StartAndWin(uint8_t cmd)
{
	gb.display.drawBitmap(9,0,game2logo);
	gb.display.setCursor(25,25);
	gb.display.setFont(font3x5);
	gb.display.print(F("level:"));
	gb.display.print(level);
	gb.display.setCursor(4,38);
	gb.display.setFont(font5x7);
	gb.display.print(F("please start!"));
	if((cmd==CMD_DOWN)||(cmd==CMD_START))
	{
			gb.gameState=GAME_STATE_RUN;
			generateMaze(10*level,10*level);
	}
	else if(cmd==CMD_LEFT)
	{
		if(level>MIN_LEVEL)
		{
			level--;
		}
	}
	else if(cmd==CMD_RIGHT)
	{
		if(level<MAX_LEVEL)
		{
			level++;
		}
	}
	else if(cmd==CMD_NUMBER)
	{
		if((cache[0]>=MIN_LEVEL)&&(cache[0]<=MAX_LEVEL))
		{
			level=cache[0];
		}
	}
}

void game2Run(uint8_t cmd)
{
			if(cmd==CMD_RETURN) {
			gb.gameState=GAME_STATE_START;
			}
			else if(cmd==CMD_START)
			{
				reset();
			}
			if (movingX == 0 && movingY == 0) {
				if (posX == width - 2 && posY == height - 2) {
					if(level<5)
					{
						gb.popup(F("  Good job!"), 50);
						level++;
					}
					else if(level>=5)
					{
						gb.popup(F("You are best!"), 50);
						level=1;
					}					
					gb.gameState=GAME_STATE_WIN;
				}				
				handleMovement(cmd);
			}
	    drawMaze();
			if (movingX < 0) movingX += 1;
			if (movingY < 0) movingY += 1;
			if (movingX > 0) movingX -= 1;
			if (movingY > 0) movingY -= 1;
}


int8_t Game::game2() {
	display.persistence=false;	
	if((gb.gameParameter1>=MIN_LEVEL)&&(gb.gameParameter1<=MAX_LEVEL))
	{
		level=gb.gameParameter1;
	}
	else
	{
		level=1;
	}
	generateMaze(10*level, 10*level);
	gb.gameState=GAME_STATE_START;
	while(1)
	{
		if(gb.update()) 
		{
			uint8_t tmpCmd=user_i2cGetCmd();
			if(state!=DIS_STATE_GAME2)
			{
				display.clear();
				display.update();
				return 1;
			}	
			switch (gb.gameState)
			{
				case GAME_STATE_START:
					game2StartAndWin(tmpCmd);
					break;
				case GAME_STATE_RUN:
					game2Run(tmpCmd);
					break;
				case GAME_STATE_WIN:
					game2StartAndWin(tmpCmd);
					break;
			}
		}	
	}
}

void reset() 
{ 
  generateMaze(10*level, 10*level);
}

void handleMovement(uint8_t cmd) {
  if ((cmd==CMD_UP) && posY > 0 && !isSet(hWalls, posY, posX)) 
  {
    if (isSet(cells, posY - 1, posX)) gridSet(cells, posY, posX, false);
    posY--;
    movingY = -6;
    gridSet(cells, posY, posX, true);
  }
  else if ((cmd==CMD_DOWN) && posY < height - 2 && !isSet(hWalls, posY + 1, posX)) 
  {
    if (isSet(cells, posY + 1, posX)) gridSet(cells, posY, posX, false);
    posY++;
    movingY = 6;
    gridSet(cells, posY, posX, true);
  }
  else if ((cmd==CMD_LEFT) && posX > 0 && !isSet(vWalls, posY, posX)) 
  {
    if (isSet(cells, posY, posX - 1)) gridSet(cells, posY, posX, false);
    posX--;
    movingX = -6;
    gridSet(cells, posY, posX, true);
  }
  else if ((cmd==CMD_RIGHT) && posX < width - 2 && !isSet(vWalls, posY, posX + 1)) 
  {
    if (isSet(cells, posY, posX + 1)) gridSet(cells, posY, posX, false);
    posX++;
    movingX = 6;
    gridSet(cells, posY, posX, true);
  }
}

void generateMaze(uint8_t w, uint8_t h) {
  width = w;
  height = h;

  for (int i = 0; i < sizeof(hWalls); i++) hWalls[i] = 0xFF;
  for (int i = 0; i < sizeof(vWalls); i++) vWalls[i] = 0xFF;
  for (int i = 0; i < sizeof(cells); i++) cells[i] = 0xFF;

  candidateCount = 0;
  moveToCell(0, 0);

  while (candidateCount > 0) {
    int randIndex = random(candidateCount);
    int choice = removeCandidate(randIndex);
    uint8_t row = (uint8_t)((choice >> 8) & 0xff);
    uint8_t col = (uint8_t)(choice & 0xff);

    bool foundWall = false;
    while (!foundWall) {
      switch (random(4)) {
        case 0:
          if (col > 0 && !isSet(cells, row, (uint8_t)(col - 1))) {
            gridSet(vWalls, row, col, false);
            foundWall = true;
          }
          break;
        case 1:
          if (col < width - 2 && !isSet(cells, row, (uint8_t)(col + 1))) {
            gridSet(vWalls, row, (uint8_t)(col + 1), false);
            foundWall = true;
          }
          break;
        case 2:
          if (row > 0 && !isSet(cells, (uint8_t)(row - 1), col)) {
            gridSet(hWalls, row, col, false);
            foundWall = true;
          }
          break;
        case 3:
          if (row < height - 2 && !isSet(cells, (uint8_t)(row + 1), col)) {
            gridSet(hWalls, (uint8_t)(row + 1), col, false);
            foundWall = true;
          }

          break;
      }
    }

    moveToCell(row, col);

    if (candidateCount == 0) {
      for (uint8_t r = 0; r < height - 1; r++) {
        for (uint8_t c = 0; c < width - 1; c++) {
          if (!isSet(cells, r, c)) moveToCell(r, c);
        }
      }
    }
  }

  gridSet(hWalls, 0, 0, false);
  gridSet(hWalls, (uint8_t)(height - 1), (uint8_t)(width - 2), false);
  posX = posY = movingX = movingY = 0;
  gridSet(cells, posX, posY, true);
}

void moveToCell(uint8_t row, uint8_t col) {
  gridSet(cells, row, col, false);
  
  if (col < width - 2 && isSet(cells, row, (uint8_t)(col + 1))) {
    addCandidate((row << 8) + (col + 1));
  }
  if (col > 0 && isSet(cells, row, (uint8_t)(col - 1))) {
    addCandidate((row << 8) + (col - 1));
  }
  if (row < height - 2 && isSet(cells, (uint8_t)(row + 1), col)) {
    addCandidate(((row + 1) << 8) + col);
  }
  if (row > 0 && isSet(cells, (uint8_t)(row - 1), col)) {
    addCandidate(((row - 1) << 8) + col);
  }
}

void addCandidate(int val) {
  if (candidateCount >= MAX_CANDIDATES) return;

  for (int i = 0; i < candidateCount; i++) {
    if (candidates[i] == val) return;
  }

  candidates[candidateCount] = val;
  candidateCount++;
}

int removeCandidate(int index) {
  int tmp = candidates[index];
  candidates[index] = candidates[candidateCount - 1];
  candidateCount--;
  return tmp;
}

void drawMaze() {
  gb.display.setColor(BLACK, BLACK);
  
  for (int row = posY >= 6 ? posY - 6 : 0; row < height && row < posY + 6; row++) {
    for (int col = posX >= 9 ? posX - 9 : 0; col < width && col < posX + 9; col++) {
      int baseX = (col - posX) * 6 + 38 + movingX;
      int baseY = (row - posY) * 6 + 20 + movingY;
      if (col < width - 1) {
        if (isSet(hWalls, row, col)) { 
          gb.display.drawFastHLine(baseX, baseY, 7);
        }

        if (row < height - 1) {
          if (isSet(cells, row, col)) {
            gb.display.fillRect(baseX + 2, baseY + 2, 3, 3);
            if (row > 0 && isSet(cells, row - 1, col) && !isSet(hWalls, row, col)) {
              gb.display.fillRect(baseX + 2, baseY - 1, 3, 3);
            }
            if (col > 0 && isSet(cells, row, col - 1) && !isSet(vWalls, row, col)) {
              gb.display.fillRect(baseX - 1, baseY + 2, 3, 3);
            }
          }
        }
      }

      if (row < height - 1) {
        if (isSet(vWalls, row, col)) {
          gb.display.drawFastVLine(baseX, baseY, 7);
        }
      }
    }
  }

  if (movingX > 0) {
    if (posX > 0 && isSet(cells, posY, posX - 1)/* && !isSet(vWalls, posY, posX) */) {
      gb.display.setColor(WHITE, WHITE);
      gb.display.fillRect(43, 22, movingX, 3);
    }
    else {
      gb.display.setColor(BLACK, BLACK);
      gb.display.fillRect(40, 22, movingX, 3);
    }
  }
  if (movingX < 0) {
    if (posX < width - 2 && isSet(cells, posY, posX + 1)/*  && !isSet(vWalls, posY, posX + 1)*/) {
      gb.display.setColor(WHITE, WHITE);
      gb.display.fillRect(40 + movingX, 22, -movingX, 3);
    }
    else {
      gb.display.setColor(BLACK, BLACK);
      gb.display.fillRect(43 + movingX, 22, -movingX, 3);
    }
  }

  if (movingY > 0) {
    if (posY > 0 && isSet(cells, posY - 1, posX)) {
      gb.display.setColor(WHITE, WHITE);
      gb.display.fillRect(40, 25, 3, movingY);
    }
    else {
      gb.display.setColor(BLACK, BLACK);
      gb.display.fillRect(40, 22, 3, movingY);
    }
  }
  if (movingY < 0) {
    if (posY < height - 2 && isSet(cells, posY + 1, posX)) {
      gb.display.setColor(WHITE, WHITE);
      gb.display.fillRect(40, 22 + movingY, 3, -movingY);
    }
    else {
      gb.display.setColor(BLACK, BLACK);
      gb.display.fillRect(40, 25 + movingY, 3, -movingY);
    }
  }
}

bool isSet(uint8_t* grid, uint8_t row, uint8_t col) {
  int offset = row * width + col;
  return (grid[offset / 8] & (1 << (7 - (offset % 8)))) != 0;
}

void gridSet(uint8_t* grid, uint8_t row, uint8_t col, bool on) {
  int offset = row * width + col;
  if (on) grid[offset / 8] |= (uint8_t)(1 << (7 - (offset % 8)));
  else grid[offset / 8] &= (uint8_t)~(1 << (7 - (offset % 8)));
}

